package deployr.core;

import static deployr.core.Streams.*;
import static java.text.MessageFormat.*;

import java.io.*;
import java.util.*;

import deployr.domain.*;

public class Workspace implements SaveOn {

	private final String user;

	private final FileSystem fileSystem;

	private final DeployListener deployListener;

	public Workspace(String user, FileSystem fileSystem) {
		this(user, fileSystem, null);
	}

	public Workspace(String user, FileSystem fileSystem, DeployListener deployListener) {
		this.user = user;
		this.fileSystem = fileSystem;
		this.deployListener = deployListener;
		mkdirs();
	}

	private void mkdirs() {
		fileSystem.mkdirs(format("/workspaces/{0}", user));
	}

	@Override
	public void save(IProject project) {
		fileSystem.mkdirs(format("/workspaces/{0}/projects/{1}", user, path(project)));
		fileSystem.put(format("/workspaces/{0}/projects/{1}/checksum", user, path(project)), project.getChecksum());
		fileSystem.put(format("/workspaces/{0}/projects/{1}/metadata", user, path(project)), new MapText(new ProjectMap(project)).toString());
	}

	private String path(IProject project) {
		return format("{0}/{1}/{2}", project.getGroupId().replaceAll("\\.", "/"), project.getArtifactId(), project.getVersion());
	}

	private String path(IUpdate update) {
		return format("{0}/{1}", update.getArtifactId(), update.getVersion());
	}

	public void store(IUpdate update) {
		fileSystem.mkdirs(format("/workspaces/{0}/projects/{1}/updates/{2}", user, path(update), update.index()));
		fileSystem.put(format("/workspaces/{0}/projects/{1}/updates/{2}/checksum", user, path(update), update.index()), update.getChecksum());
		fileSystem.put(format("/workspaces/{0}/projects/{1}/updates/{2}/diff", user, path(update), update.index()), update.getDiffContent());
		fileSystem.put(format("/workspaces/{0}/projects/{1}/updates/{2}/metadata", user, path(update), update.index()), new MapText(new UpdateMap(update)).toString());
	}

	public void store(IDeliverable deliverable) {
		fileSystem.mkdirs(format("/workspaces/{0}/assets", user));
		fileSystem.put(format("/workspaces/{0}/assets/{1}", user, deliverable.getMd5()), deliverable.getContent());
	}

	public IUpdate findUpdate(String updateKey) {
		List<String> somePaths = metadataPaths("");
		for (String path : somePaths) {
			Metadata metadata = new Metadata(fileSystem.read(path));

			if (metadata.updateKeyIs(updateKey)) {
				Update update = new Update(metadata);
				update.updateChecksum(slurp(fileSystem.read(format("/workspaces/{0}/projects/{1}/updates/{2}/checksum", user, path(update), update.index()))));
				return update;
			}
		}
		return NotFound.NotFoundUpdate;
	}

	public IUpdate createUpdate(String projectKey) {
		int index = nextUpdateIndex(projectKey);
		List<String> somePaths = metadataPaths("");
		for (String path : somePaths) {
			Metadata metadata = new Metadata(fileSystem.read(path));
			if (metadata.projectKeyIs(projectKey)) {
				return new Update(metadata, index);
			}
		}
		return NotFound.NotFoundUpdate;
	}

	private int nextUpdateIndex(String projectKey) {
		List<String> somePaths = fileSystem.find(format("/workspaces/{0}/projects", user), "metadata");
		int count = 0;
		for (String path : somePaths) {
			InputStream inputStream = fileSystem.read(path);
			try {
				Metadata metadata = new Metadata(inputStream);
				if (metadata.projectKeyIs(projectKey)) {
					count++;
				}
			} finally {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return count;
	}

	public void removeUpdates(String projectKey) {
		fileSystem.delete(format("/workspaces/{0}/projects/{1}/updates", user, projectKey));
	}

	public boolean exist(String relativePath) {
		return fileSystem.exist(format("/workspaces/{0}/{1}", user, relativePath));
	}

	public List<Metadata> projectsMetadata() {
		List<String> somePaths = metadataPaths("");
		List<Metadata> result = new LinkedList<Metadata>();
		for (String path : somePaths) {
			InputStream inputStream = fileSystem.read(path);
			try {
				relatedToProjectMetadata(inputStream, result);
			} finally {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	private List<String> metadataPaths(String path) {
		return fileSystem.find(format("/workspaces/{0}/projects/{1}", user, path), "metadata");
	}

	private void relatedToProjectMetadata(InputStream inputStream, List<Metadata> result) {
		Metadata metadata = newMetadata(inputStream);
		if (metadata.relatedToProject()) result.add(metadata);
	}

	public Projects scanProjects() {
		return new ProjectBuilder(this).scan().getProjects();
	}

	public List<IUpdate> scanUpdates(String projectPath) {
		return new UpdateBuilder(this).scan(projectPath).getUpdates();
	}

	public List<Metadata> updatesMetadata(String projectPath) {
		List<String> somePaths = metadataPaths(projectPath);
		List<Metadata> result = new LinkedList<Metadata>();
		for (String path : somePaths) {
			relatedToUpdateMetadata(fileSystem.read(path), result);
		}
		return result;
	}

	private void relatedToUpdateMetadata(InputStream inputStream, List<Metadata> result) {
		Metadata metadata = newMetadata(inputStream);
		if (metadata.relatedToUpdate()) result.add(metadata);
	}

	private Metadata newMetadata(InputStream inputStream) {
		return new Metadata(inputStream);
	}

	public FileSystem getAssets() {
		return new FileSystem(file(fileSystem.getFile(), format("/workspaces/{0}/assets", user)));
	}

	public FileSystem getWebapps() {
		return new FileSystem(file(fileSystem.getFile(), format("/workspaces/{0}/webapps", user)));
	}

	public String checksum(String path) {
		return slurp(fileSystem.read(format("/workspaces/{0}/projects/{1}/checksum", user, path)));
	}

	public void deploy(IProject project) {
		Deploy deploy = new Deploy(getAssets(), project);
		deploy.attach(deployListener);
		deploy.warTo(getWebapps());
	}

	public void deploy(IUpdate update) {
		Deploy deploy = new Deploy(getAssets(), update);
		deploy.attach(deployListener);
		deploy.warTo(getWebapps());
	}
}
